package chapter_11;

// 多线程编程

/*
11.1 Java线程模型
线程存在于好几种状态。线程可以正在运行（running）。只要获得CPU时间它就可以
运行。运行的线程可以被挂起（suspend），并临时中断它的执行。一个挂起的线程可以被
恢复（resume，允许它从停止的地方继续运行。一个线程可以在等待资源时被阻塞（block）。
在任何时候，线程可以终止（terminate），这立即中断了它的运行。一旦终止，线程不能
被恢复。

    11.1.1 线程优先级

    11.1.2 同步性

    11.1.3 消息传递

    11.1.4 Thread类和Runable接口
    Java的多线程系统建立于Thread类，它的方法，它的共伴接口Runnable基础上。Thread
    类封装了线程的执行。既然你不能直接引用运行着的线程的状态，你要通过它的代理处理
    它，于是Thread 实例产生了。为创建一个新的线程，你的程序必须扩展Thread 或实现
    Runnable接口。
    Thread类定义了好几种方法来帮助管理线程。

    方法 意义
    getName 获得线程名称
    getPriority 获得线程优先级
    jsAlive 判定线程是否仍在运行
    join 等待一个线程终止
    run 线程的入口点.
    sleep 在一段时间内挂起线程
    start 通过调用运行方法来启动线程
 */


/*
11.2 主线程
当Java程序启动时，一个线程立刻运行，该线程通常叫做程序的主线程（main thread），
因为它是程序开始时就执行的。主线程的重要性体现在两方面：
· 它是产生其他子线程的线程
· 通常它必须最后完成执行，因为它执行各种关闭动作。
尽管主线程在程序启动时自动创建，但它可以由一个Thread对象控制。为此，你必须
调用方法currentThread()获得它的一个引用，currentThread()是Thread类的公有的静态成员。
它的通常形式如下：
static Thread currentThread( )
该方法返回一个调用它的线程的引用。一旦你获得主线程的引用，你就可以像控制其
他线程那样控制主线程。
 */


/*
11.3 创建线程
大多数情况下，通过实例化一个Thread对象来创建一个线程。Java定义了两种方式：
· 实现Runnable 接口。
· 可以继承Thread类。

    11.3.1 实现Runnable接口
    创建线程的最简单的方法就是创建一个实现Runnable 接口的类。Runnable抽象了一个
    执行代码单元。你可以通过实现Runnable接口的方法创建每一个对象的线程。为实现
    Runnable 接口，一个类仅需实现一个run()的简单方法，该方法声明如下：
    public void run( )
    在run()中可以定义代码来构建新的线程。理解下面内容是至关重要的：run()方法能够
    像主线程那样调用其他方法，引用其他类，声明变量。仅有的不同是run()在程序中确立另
    一个并发的线程执行入口。当run()返回时，该线程结束。
    在你已经创建了实现Runnable接口的类以后，你要在类内部实例化一个Thread类的对
    象。Thread 类定义了好几种构造函数。我们会用到的如下：
    Thread(Runnable threadOb, String threadName)
    该构造函数中，threadOb是一个实现Runnable接口类的实例。这定义了线程执行的起点。
    新线程的名称由threadName定义。
    建立新的线程后，它并不运行直到调用了它的start()方法，该方法在Thread 类中定义。
    本质上，start() 执行的是一个对run()的调用。 Start（）方法声明如下：
    Thread(Runnable threadOb, String threadName)
    该构造函数中，threadOb是一个实现Runnable接口类的实例。这定义了线程执行的起点。
    新线程的名称由threadName定义。
    建立新的线程后，它并不运行直到调用了它的start()方法，该方法在Thread 类中定义。
    本质上，start() 执行的是一个对run()的调用。 Start（）方法声明如下：
    void start( )

    11.3.2 扩展Thread
    当一个类继承Thread时，它必须重载run()方法，这个run()方法是新线程的入口。它也必
    须调用start()方法去启动新线程执行。

    11.3.3 选择合适的方法
    Thread类定义了多种方法可以被派生类重载。对于所有的方法，惟一的必须被重载的是
    run()方法。这当然是实现Runnable接口所需的同样的方法。很多Java程序员认为类仅
    在它们被加强或修改时应该被扩展。因此，如果你不重载Thread的其他方法时，最好只实
    现Runnable 接口。
 */


/*
11.4 创建多线程
 */


/*
11.5 使用isAlive()和join()
有两种方法可以判定一个线程是否结束。第一，可以在线程中调用isAlive()。这种方法
由Thread定义，它的通常形式如下：
final boolean isAlive( )
如果所调用线程仍在运行，isAlive()方法返回true，如果不是则返回false。
但isAlive（）很少用到，等待线程结束的更常用的方法是调用join()，描述如下：
final void join( ) throws InterruptedException
该方法等待所调用线程结束。该名字来自于要求线程等待直到指定线程参与的概念。
join()的附加形式允许给等待指定线程结束定义一个最大时间。
 */


/*
11.6 线程优先级
线程优先级被线程调度用来判定何时每个线程允许运行。理论上，优先级高的线程比
优先级低的线程获得更多的CPU时间。实际上，线程获得的CPU时间通常由包括优先级在
内的多个因素决定（例如，一个实行多任务处理的操作系统如何更有效的利用CPU时间）。
一个优先级高的线程自然比优先级低的线程优先。举例来说，当低优先级线程正在运行，
而一个高优先级的线程被恢复（例如从沉睡中或等待I/O中），它将抢占低优先级线程所使
用的CPU。
设置线程的优先级，用setPriority()方法，该方法也是Tread 的成员。它的通常形式为：
final void setPriority(int level)
这里， level 指定了对所调用的线程的新的优先权的设置。Level 的值必须在
MIN_PRIORITY到MAX_PRIORITY范围内。通常，它们的值分别是1和10。要返回一个线
程为默认的优先级，指定NORM_PRIORITY，通常值为5。这些优先级在Thread中都被定义
为final型变量。
你可以通过调用Thread的getPriority()方法来获得当前的优先级设置。该方法如下：
final int getPriority( )
 */


/*
11.7 线程同步
同步的关键是管程（也叫信号量semaphore）的概念。管程是一个互斥独占锁定的对象，
或称互斥体（mutex）。在给定的时间，仅有一个线程可以获得管程。当一个线程需要锁定，
它必须进入管程。所有其他的试图进入已经锁定的管程的线程必须挂起直到第一个线程退
出管程。这些其他的线程被称为等待管程。一个拥有管程的线程如果愿意的话可以再次进
入相同的管程。
    11.7.1 使用同步方法
    Java中同步是简单的，因为所有对象都有它们与之对应的隐式管程。进入某一对象的
    管程，就是调用被synchronized关键字修饰的方法。当一个线程在一个同步方法内部，所有
    试图调用该方法（或其他同步方法）的同实例的其他线程必须等待。为了退出管程，并放
    弃对对象的控制权给其他等待的线程，拥有管程的线程仅需从同步方法中返回。
    任何时候在多线程情况下，你有一个方法或多个方法操纵对象的内部状态，都必须用
    synchronized 关键字来防止状态出现竞争。记住，一旦线程进入实例的同步方法，没有其
    他线程可以进入相同实例的同步方法。然而，该实例的其他不同步方法却仍然可以被调用。

    11.7.2 同步语句
    尽管在创建的类的内部创建同步方法是获得同步的简单和有效的方法，但它并非在任
    何时候都有效。这其中的原因，请跟着思考。假设你想获得不为多线程访问设计的类对象
    的同步访问，也就是，该类没有用到synchronized方法。而且，该类不是你自己，而是第三
    方创建的，你不能获得它的源代码。这样，你不能在相关方法前加synchronized修饰符。怎
    样才能使该类的一个对象同步化呢？很幸运，解决方法很简单：你只需将对这个类定义的
    方法的调用放入一个synchronized块内就可以了。
    下面是synchronized语句的普通形式：
    synchronized(object) {
    // statements to be synchronized
    }
    其中，object是被同步对象的引用。如果你想要同步的只是一个语句，那么不需要花括
    号。一个同步块确保对object成员方法的调用仅在当前线程成功进入object管程后发生。
 */


/*
11.8 线程间通信
为避免轮询，Java包含了通过wait( )，notify( )和notifyAll( )方法实现的一个进程间通信
机制。这些方法在对象中是用final方法实现的，所以所有的类都含有它们。这三个方法仅
在synchronized方法中才能被调用。尽管这些方法从计算机科学远景方向上来说具有概念的
高度先进性，实际中用起来是很简单的：
· wait( ) 告知被调用的线程放弃管程进入睡眠直到其他线程进入相同管程并且调用notify( )。
· notify( ) 恢复相同对象中第一个调用 wait( ) 的线程。
· notifyAll( ) 恢复相同对象中所有调用 wait( ) 的线程。具有最高优先级的线程最先运行。
这些方法在Object中被声明，如下所示：
final void wait( ) throws InterruptedException
final void notify( )
final void notifyAll( )

11.8.1 死锁
需要避免的与多任务处理有关的特殊错误类型是死锁（deadlock）。死锁发生在当两个
线程对一对同步对象有循环依赖关系时。例如，假定一个线程进入了对象X的管程而另一
个线程进入了对象Y的管程。如果X的线程试图调用Y的同步方法，它将像预料的一样被锁
定。而Y的线程同样希望调用X的一些同步方法，线程永远等待，因为为到达X，必须释放
自己的Y的锁定以使第一个线程可以完成。死锁是很难调试的错误，因为：
· 通常，它极少发生，只有到两线程的时间段刚好符合时才能发生。
· 它可能包含多于两个的线程和同步对象（也就是说，死锁在比上述的例子有更多
复杂的事件序列的时候可以发生）。
 */


/*
11.9 挂起、恢复和终止线程
有时，线程的挂起是很有用的。例如，一个独立的线程可以用来显示当日的时间。如
果用户不希望用时钟，线程被挂起。在任何情形下，挂起线程是很简单的，一旦挂起，重
新启动线程也是一件简单的事。
 */


class CurrentThreadDemo {
    // 11.2
    public static void current() {
        Thread t = Thread.currentThread();
        System.out.println("Current thread: " + t);
        /*
        默认情况下，主线程的名称是main。它的优先级是5，这也是默认值，main也是
        所属线程组的名称。一个线程组（thread group）是一种将线程作为一个整体集合的状态控
        制的数据结构。
         */
        //change the name of the thread
        t.setName("My Thread");
        System.out.println("After name change: " + t);

        try {
            for (int n = 5; n > 0; n--) {
                System.out.println(n);
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            System.out.println("Main thread interrupted");
        }
    }
}


class NewThread implements Runnable {
    // 11.3.1
    Thread t;

    NewThread() {
        // Create a new, second thread
        t = new Thread(this, "Demo Thread");
        t.start();
    }

    // This is the entry point for the second thread.
    public void run() {
        // start()执行的是一个对run()的调用。
        try {
            for (int i = 5; i > 0; i--) {
                System.out.println("Child Thread: " + i);
                Thread.sleep(500);
            }
        } catch (InterruptedException e) {
            System.out.println("Child interrupted.");
        }
        System.out.println("Exiting child thread.");
    }
}


// Create a second thread by extending THread
class NewThread2 extends Thread {
    // 11.3.2
    NewThread2() {
        // Create a new, second thread
        super("Demo Thread");
        // super()调用了下列形式的Thread构造函数：
        // public Thread(String threadName)
        System.out.println("Child thread: " + this);
        start();
    }

    // This is the entry ponit for the second thread.
    public void run() {
        // 必须重载
        try {
            for (int i = 5; i > 0; i--) {
                System.out.println("Child Thread: " + i);
                Thread.sleep(500);
            }
        } catch (InterruptedException e) {
            System.out.println("Child interrupted.");
        }
        System.out.println("Exiting child thread.");
    }
}


class NewThread3 implements Runnable {
    // 11.5
    String name;
    Thread t;

    NewThread3(String threadname) {
        name = threadname;
        t = new Thread(this, name);
        t.start();
    }

    public void run() {
        try {
            for (int i = 5; i > 0; i--) {
                System.out.println(name + ": " + i);
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            System.out.println(name + " interrupted.");
        }
        System.out.println(name + " exiting.");
    }
}


class clicker implements Runnable {
    // 11.6
    int click = 0;
    Thread t;
    /*
    如果不用volatile，Java可以自由的优化循环：running的值被存在CPU的一个寄存器中，
    每次重复不一定需要复检。volatile的运用阻止了该优化，告知Java running可以改变，改变
    方式并不以直接代码形式显示。
     */
    private volatile boolean running = true;

    public clicker(int p) {
        t = new Thread(this);
        t.setPriority(p);
    }

    public void run() {
        while (running)
            click++;
    }

    public void stop() {
        running = false;
    }

    public void start() {
        t.start();
    }
}


class Callme {
    // 11.7
    void call(String msg) {
//    synchronized void call(String msg) {
        /*
        通过调用sleep( )，call( )方法允许执行转换到另一个线程。该结果是三个消
        息字符串的混合输出。该程序中，没有阻止三个线程同时调用同一对象的同一方法的方法
        存在。这是一种竞争，因为三个线程争着完成方法。例题用sleep( )使该影响重复和明显。
        在大多数情况，竞争是更为复杂和不可预知的，因为你不能确定何时上下文转换会发生。
        这使程序时而运行正常时而出错。为达到所想达到的目的，必须有权连续的使用call( )。
        也就是说，在某一时刻，必须限制只有一个线程可以支配它。为此，你只需在call( )
        定义前加上关键字synchronized，
         */
        System.out.print("[" + msg);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println("Interrupted");
        }
        System.out.println("]");
    }
}


class Caller implements Runnable {
    // 11.7
    String msg;
    Callme target;
    Thread t;

    public Caller(Callme targ, String s) {
        target = targ;
        msg = s;
        t = new Thread(this);
        t.start();
    }

    public void run() {
//        synchronized (target) { // synchronized block
//            target.call(msg);
//        }
        target.call(msg);
    }
}


class Q {
    // 11.8
    int n;
    boolean valueSet = false;

    synchronized int get() {
        if(!valueSet)
            try {
                wait();
            } catch(InterruptedException e) {
                System.out.println("InterruptedException caught");
            }
        System.out.println("Got: " + n);
        valueSet = false;
        notify();
        return n;
    }

    synchronized void put(int n) {
        if (valueSet)
            try {
                wait();
            } catch (InterruptedException e) {
                System.out.println("InterruptedException caught");
            }

        this.n = n;
        valueSet = true;
        System.out.println("Put: " + n);
        notify();
    }
}


class Producer implements Runnable {
    // 11.8
    Q q;

    Producer(Q q) {
        this.q = q;
        new Thread(this, "Producer").start();
    }

    public void run() {
        int i = 0;

        while (i < 6)
            q.put(i++);
    }
}


class Consumer implements Runnable {
    // 11.8
    Q q;

    Consumer(Q q) {
        this.q = q;
        new Thread(this, "Consumer").start();
    }

    public void run() {
        int n = 0;
        while (n < 6) {
            q.get();
            n++;
        }
    }
}


class NewThread4 implements Runnable {
    // 11.9
    String name;    // name of thread
    Thread t;
    boolean suspendFlag;

    NewThread4(String threadname) {
        name = threadname;
        t = new Thread(this, name);
        System.out.println("New thread: " + t);
        suspendFlag = false;
        t.start();  // Start the thread
    }

    public void run() {
        try {
            for (int i = 15; i > 0; i--) {
                System.out.println(name + ": " + i);
                Thread.sleep(200);
                synchronized (this) {
                    while (suspendFlag) {
                        wait();
                    }
                }
            }
        } catch (InterruptedException e) {
            System.out.println(name + " interrupted.");
        }
        System.out.println(name + " exiting.");
    }

    void mysuspend() {
        suspendFlag = true;
    }

    synchronized void myresume() {
        suspendFlag = false;
        notify();
    }

    public static void thisRun() {
        NewThread4 ob1 = new NewThread4("One");
        NewThread4 ob2 = new NewThread4("Two");

        try {
            Thread.sleep(1000);
            ob1.mysuspend();
            System.out.println("Suspending thread One");
            Thread.sleep(1000);
            ob1.myresume();
            System.out.println("Resuming thread One");
            ob2.mysuspend();
            System.out.println("Suspending thread Two");
            Thread.sleep(1000);
            ob2.myresume();
            System.out.println("Resuming thread Two");
        } catch (InterruptedException e) {
            System.out.println("Main thread Interrupted");
        }

        // wait for threads to finish
        try {
            System.out.println("Waiting for threads to finish.");
            ob1.t.join();
            ob2.t.join();
        } catch (InterruptedException e) {
            System.out.println("Main thread Interrupted");
        }
        System.out.println("Main thread exiting.");
    }
}


public class chapter_11 {

    public static void main(String[] args) {
        // 11.2
//        CurrentThreadDemo.current();

        // 11.3
//        new NewThread();
////        new NewThread2();
//        try {
//            for (int i = 5; i > 0; i--) {
//                System.out.println("Main Thread: " + i);
//                Thread.sleep(1000);
//            }
//        } catch (InterruptedException e) {
//            System.out.println("Main thread interrupted.");
//        }
//        System.out.println("Main thread exiting.");

        // 11.5
//        NewThread3 ob1 = new NewThread3("One");
//        NewThread3 ob2 = new NewThread3("Two");
//        NewThread3 ob3 = new NewThread3("Three");
//        System.out.println("Thread One is alive: " + ob1.t.isAlive());
//        System.out.println("Thread Two is alive: " + ob2.t.isAlive());
//        System.out.println("Thread Three is alive: " + ob3.t.isAlive());
//        // wait for threads to finish
//        try {
//            System.out.println("Waiting for threads to finish.");
//            ob1.t.join();
//            ob2.t.join();
//            ob3.t.join();
//        } catch (InterruptedException e) {
//            System.out.println("Main thread Interrupted");
//        }
//        System.out.println("Thread One is alive: " + ob1.t.isAlive());
//        System.out.println("Thread Two is alive: " + ob2.t.isAlive());
//        System.out.println("Thread Three is alive: " + ob3.t.isAlive());
//        System.out.println("Main thread exiting.");

        // 11.6
//        Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
//        clicker hi = new clicker(Thread.NORM_PRIORITY + 2);
//        clicker lo = new clicker(Thread.NORM_PRIORITY -2);
//        hi.start();
//        lo.start();
//        try {
//            Thread.sleep(10000);
//        } catch (InterruptedException e) {
//            System.out.println("Main thread interrupted.");
//        }
//        hi.stop();
//        lo.stop();
//
//        // Wait for child threads to terminate
//        try {
//            hi.t.join();
//            lo.t.join();
//        } catch (InterruptedException e) {
//            System.out.println("InterruptedException caught");
//        }
//        System.out.println("Low-priority thread: " + lo.click);
//        System.out.println("High-priority thread: " + hi.click);

        // 11.7
//        Callme target = new Callme();
//        Caller ob1 = new Caller(target, "Hello");
//        Caller ob2 = new Caller(target, "Synchronized");
//        Caller ob3 = new Caller(target, "World");
//        // wait for threads to end
//        try {
//            ob1.t.join();
//            ob2.t.join();
//            ob3.t.join();
//        } catch (InterruptedException e) {
//            System.out.println("Interrupted");
//        }

        // 11.8
        /*
        内部get( ), wait( )被调用。这使执行挂起直到Producer 告知数据已经预备好。这时，内
        部get( ) 被恢复执行。获取数据后，get( )调用notify( )。这告诉Producer可以向序列中输入
        更多数据。在put( )内，wait( )挂起执行直到Consumer取走了序列中的项目。当执行再继续，
        下一个数据项目被放入序列，notify( )被调用，这通知Consumer它应该移走该数据。
         */
//        Q q = new Q();
//        new Producer(q);
//        new Consumer(q);
//        System.out.println("Producer and Consumer: ");

        // 11.9
        NewThread4.thisRun();
    }
}
